from typing import List

from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from fastapi import Request, Response
from starlette.requests import HTTPConnection
from starlette.types import Receive, Scope, Send, ASGIApp


class MissingTenantError(Exception):
    ...


class TenantCheckMiddleware(BaseHTTPMiddleware):

    def __init__(self, app: ASGIApp, white_path_list: List[str] = None, white_path_prefix: List[str] = None):
        super().__init__(app)
        self.white_path_list = white_path_list or []
        self.white_path_prefix = white_path_prefix or []

    async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
        response = await call_next(request)
        return response

    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
        from common.utils.resp import JsonResponse, RC
        from common.g.g import G_TENANT_BLOOMFILTER
        if scope["type"] not in ["http", "websocket"]:
            await self.app(scope, receive, send)
            return
        conn = HTTPConnection(scope)
        path = conn.url.path
        # 1. 判断是否在白名单
        if path in self.white_path_list:
            await self.app(scope, receive, send)
            return
        for prefix in self.white_path_prefix:
            if path.startswith(prefix):
                await self.app(scope, receive, send)
                return
        else:
            # 2. 判断是否已经选择tenant登陆
            tenant_id = conn.cookies.get("login_tenant")
            if tenant_id is None or not tenant_id.isdigit():
                resp = JsonResponse(code=RC.NEED_LOGIN_TENANT, msg="先选择租户登陆").to_response()
                await resp(scope, receive, send)
                return

            # 3. 判断tenant是否存在
            exist = await G_TENANT_BLOOMFILTER.exist(f"tenant:{tenant_id}")
            if not exist:
                resp = JsonResponse(code=RC.TENANT_NOT_EXIST, msg="租户不存在").to_response()
                await resp(scope, receive, send)
                return

            # 4. 业务
            conn.state.tenant_id = tenant_id
            await self.app(scope, receive, send)
            return
